package cn.jetbrain.netbeans.pdfviewer.view;

import cn.jetbrain.netbeans.pdfviewer.PDFViewer;
import com.sun.pdfview.PDFPage;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.ImageObserver;

import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.UIManager;

public class PagePanel extends JPanel implements ImageObserver, MouseListener, MouseMotionListener {

    Image currentImage;
    PDFPage currentPage;
    Dimension prevSize;
    Flag flag = new Flag();
    AffineTransform af;
    double zoomFactor = -1.0;
    double scale = 1.0;
    public final static double MAX_SCALE = 5;
    public final static double MIN_SCALE = 0.2;
    JViewport jvp = null;    
    public static final int SCROLLBAR_BORDER = 2;
    public static final int SCROLLBAR_WIDTH = SCROLLBAR_BORDER + (Integer) UIManager.get("ScrollBar.width");

    public PagePanel() {
        super();
        setFocusable(true);
        addMouseListener(this);
        addMouseMotionListener(this);
        setBackground(PDFViewer.DEFAULT_BACKGROUND);
        setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    }

    /**
     * Stop the generation of any previous page, and draw the new one.
     * @param page the PDFPage to draw.
     */
    public synchronized void showPage(PDFPage page) {
        // stop drawing the previous page
        if (currentPage != null && prevSize != null) {
            currentPage.stop(prevSize.width, prevSize.height, null);
        }

        // set up the new page
        currentPage = page;
        if (page == null) {
            // no page
            currentImage = null;
            af = null;
            repaint();
        } else {
            // start drawing -- clear the flag to indicate we're in progress.
            flag.clear();

            Dimension sz = getSize();

            if (this.getParent().getParent() instanceof JScrollPane) {
                sz = ((JScrollPane) this.getParent().getParent()).getSize();
                sz = new Dimension(sz.width - SCROLLBAR_BORDER, sz.height - SCROLLBAR_BORDER);
            }

            if (sz.width + sz.height <= 0) {
                // no image to draw.
                return;
            }

            Dimension pageSize = page.getUnstretchedSize(sz.width, sz.height, null);

            // get the new image
            int imw = pageSize.width;
            int imh = pageSize.height;

            int scrollbarWidth = 0;
            if (zoomFactor == -1.0) {//fit page
                scale = Math.min(sz.getWidth() / imw, sz.getHeight() / imh);
                imw *= scale;
                imh *= scale;
                setSize(sz.width, sz.height);
                setPreferredSize(getSize());
            } else if (zoomFactor == -2.0) {//fit h
                if ((sz.height * page.getAspectRatio()) > sz.width) {
                    scrollbarWidth = SCROLLBAR_WIDTH;
                } else {
                    scrollbarWidth = 0;
                }
                scale = (sz.getHeight() - scrollbarWidth) / imh;
                imh = sz.height - scrollbarWidth;
                imw = (int) (imw * scale);
                setSize(Math.max(imw, sz.width), imh);
                setPreferredSize(getSize());
            } else if (zoomFactor == -3.0) {//fit w
                if (sz.width > (sz.height * page.getAspectRatio())) {
                    scrollbarWidth = SCROLLBAR_WIDTH;
                } else {
                    scrollbarWidth = 0;
                }
                scale = (sz.getWidth() - scrollbarWidth) / imw;
                imw = sz.width - scrollbarWidth;
                imh = (int) (imh * scale);
                setSize(imw, Math.max(imh, sz.height));
                setPreferredSize(getSize());
            } else if (zoomFactor == -4.0) {                
                imw=(int) page.getWidth();
                imh=(int) page.getHeight();
                scale = Math.min(sz.getWidth() / imw, sz.getHeight() / imh);
                setSize(Math.max(imw, imh > sz.height ? sz.width - SCROLLBAR_WIDTH : sz.width), Math.max(imh, imw > sz.width ? sz.height - SCROLLBAR_WIDTH : sz.height));
                setPreferredSize(getSize());
            } else if (zoomFactor > 0) {
                scale = zoomFactor;
                imw *= zoomFactor;
                imh *= zoomFactor;
                setSize(Math.max(imw, imh > sz.height ? sz.width - SCROLLBAR_WIDTH : sz.width), Math.max(imh, imw > sz.width ? sz.height - SCROLLBAR_WIDTH : sz.height));
                setPreferredSize(getSize());
            }
            currentImage = page.getImage(imw, imh, null, this, true, true);


            prevSize = pageSize;


            repaint();

            if(this.getParent() instanceof JViewport){
                ((JViewport)this.getParent()).setViewPosition(new Point(0,0));
            }
        }
    }

    @Override
    public void paint(Graphics g1) {
        Graphics2D g2d = (Graphics2D) g1;
        g2d.setColor(getBackground());
        g2d.fillRect(0, 0, getWidth(), getHeight());
        if (currentImage == null) {
            g2d.setColor(Color.black);
        } else {
            // draw the image
            int imw = currentImage.getWidth(null);
            int imh = currentImage.getHeight(null);

            // draw it centered within the panel
            int offx = (getWidth() - imw) / 2;
            int offy = (getHeight() - imh) / 2;
            if (offx < 0) {
                offx = 0;
            }
            if (offy < 0) {
                offy = 0;
            }
            g2d.drawImage(currentImage, offx, offy, this);
            af = null;
        }
        g2d.dispose();
        requestFocus();
    }

    public void setZoom(double zf) {
        if (zf > 1) {
            this.zoomFactor = Math.min(zf, MAX_SCALE);
        } else if (zf < 1 && zf > 0) {
            this.zoomFactor = Math.max(zf, MIN_SCALE);
        } else if (zf == -4.0 || zf == -1.0 || zf == -2.0 || zf == -3.0) {
            this.zoomFactor = zf;
        }
        showPage(currentPage);
    }

    public double getScale() {
        return this.scale;
    }

    public PDFPage getPage() {
        return currentPage;
    }

    public Dimension getCurSize() {
        return prevSize;
    }

    public void waitForCurrentPage() {
        flag.waitForFlag();
    }
    /** x location of the mouse-down event */
    int downx;
    /** y location of the mouse-down event */
    int downy;

    /** Handles a mousePressed event */
    public void mousePressed(MouseEvent evt) {
        downx = evt.getX();
        downy = evt.getY();
    }

    public void mouseReleased(MouseEvent evt) {
    }

    public void mouseClicked(MouseEvent evt) {
    }

    public void mouseEntered(MouseEvent evt) {
    }

    public void mouseExited(MouseEvent evt) {
    }

    public void mouseMoved(MouseEvent evt) {
    }

    public void mouseDragged(MouseEvent evt) {
        if (!(this.getParent() instanceof JViewport)) {
            return;
        }
        int x = evt.getX();
        int y = evt.getY();
        int dx = x - downx;
        int dy = y - downy;

        if (jvp == null) {
            jvp = (JViewport) this.getParent();
        }
        x = Math.max(jvp.getViewPosition().x - dx, 0);
        y = Math.max(jvp.getViewPosition().y - dy, 0);
        x = Math.min(x, getWidth() - jvp.getViewRect().width);
        y = Math.min(y, getHeight() - jvp.getViewRect().height);
        jvp.setViewPosition(new Point(x, y));
    }
}
